/*

 Name: LOAD_FAR.C

 Description:
 Farandole (FAR) module loader

 Portability:
 All systems - all compilers (hopefully)

 If this module is found to not be portable to any particular platform,
 please contact Jake Stine at dracoirs@epix.net (see MIKMOD.TXT for
 more information on contacting the author).
*/

#include <string.h>
#include "mikmod.h"


typedef struct FARSAMPLE
{   CHAR  samplename[32];
    ULONG length;
    UBYTE finetune;
    UBYTE volume;
    ULONG reppos;
    ULONG repend;
    UBYTE type;
    UBYTE loop;
} FARSAMPLE;



typedef struct FARHEADER1
{   UBYTE id[4];                        /* file magic */
    CHAR  songname[40];                 /* songname */
    CHAR  blah[3];                      /* 13,10,26 */
    UWORD headerlen;                    /* remaining length of header in bytes */
    UBYTE version;
    UBYTE onoff[16];
    UBYTE edit1[9];
    UBYTE speed;
    UBYTE panning[16];
    UBYTE edit2[4];
    UWORD stlen;
} FARHEADER1;


typedef struct FARHEADER2
{   UBYTE orders[256];
    UBYTE numpat;
    UBYTE snglen;
    UBYTE loopto;
    UWORD patsiz[256];
} FARHEADER2;


typedef struct FARNOTE
{   UBYTE note,ins,vol,eff;
} FARNOTE;



static CHAR FAR_Version[] = "Farandole";
static FARHEADER1 *mh1 = NULL;
static FARHEADER2 *mh2 = NULL;
static FARNOTE *pat = NULL;


BOOL FAR_Test(void)
{
    UBYTE id[4];

    if(!_mm_read_UBYTES(id,4,modfp)) return 0;
    return(!memcmp(id,"FAR=",4));
}


BOOL FAR_Init(void)
{
    if(!(mh1 = (FARHEADER1 *)_mm_malloc(sizeof(FARHEADER1)))) return 0;
    if(!(mh2 = (FARHEADER2 *)_mm_malloc(sizeof(FARHEADER2)))) return 0;
    if(!(pat = (FARNOTE *)_mm_malloc(16*256*sizeof(FARNOTE)))) return 0;

    return 1;
}


void FAR_Cleanup(void)
{
    if(mh1!=NULL) free(mh1);
    if(mh2!=NULL) free(mh2);
    if(pat!=NULL) free(pat);

    mh1 = NULL;
    mh2 = NULL;
    pat = NULL;
}


UBYTE *FAR_ConvertTrack(FARNOTE *n,int rows)
{
       int t;

       UniReset();

       for(t=0; t<rows; t++)
       {   if(n->note)
           {   UniInstrument(n->ins);
               UniNote(n->note+23+12);
           }

           if(n->vol&0xf) UniPTEffect(0xc,(n->vol&0xf)<<2);
           switch(n->eff>>4)
           {   case 0xf:
                  UniPTEffect(0xf,n->eff&0xf);
               break;

               /* others not yet implemented */
           }

           UniNewline();
           n+=16;
       }
       return UniDup();
}


BOOL FAR_Load(void)
{
       int t,u,tracks=0;
       SAMPLE     *q;
       FARSAMPLE  s;
       FARNOTE    *crow;
       UBYTE      smap[8];

       /* try to read module header (first part) */
       _mm_read_UBYTES(mh1->id,4,modfp);
       _mm_read_SBYTES(mh1->songname,40,modfp);
       _mm_read_SBYTES(mh1->blah,3,modfp);
       mh1->headerlen = _mm_read_I_UWORD (modfp);
       mh1->version   = _mm_read_UBYTE (modfp);
       _mm_read_UBYTES(mh1->onoff,16,modfp);
       _mm_read_UBYTES(mh1->edit1,9,modfp);
       mh1->speed     = _mm_read_UBYTE(modfp);
       _mm_read_UBYTES(mh1->panning,16,modfp);
       _mm_read_UBYTES(mh1->edit2,4,modfp);
       mh1->stlen     = _mm_read_I_UWORD (modfp);


       /* init modfile data */

       of.modtype   = strdup(FAR_Version);
       of.songname  = DupStr(mh1->songname,40);
       of.numchn    = 16;
       of.initspeed = mh1->speed;
       of.inittempo = 99;

       for(t=0; t<16; t++) of.panning[t] = mh1->panning[t]<<4;

       /* read songtext into comment field */
       if(!ReadComment(mh1->stlen)) return 0;

       /* try to read module header (second part) */
       _mm_read_UBYTES(mh2->orders,256,modfp);
       mh2->numpat        = _mm_read_UBYTE(modfp);
       mh2->snglen        = _mm_read_UBYTE(modfp);
       mh2->loopto        = _mm_read_UBYTE(modfp);
       _mm_read_I_UWORDS(mh2->patsiz,256,modfp);

/*      of.numpat=mh2->numpat; */
       of.numpos = mh2->snglen;
       if(!AllocPositions(of.numpos)) return 0;
       for(t=0; t<of.numpos; t++)
       {   if(mh2->orders[t]==0xff) break;
           of.positions[t] = mh2->orders[t];
       }

       /* count number of patterns stored in file */
       of.numpat = 0;
       for(t=0; t<256; t++)
          if(mh2->patsiz[t]) if((t+1)>of.numpat) of.numpat=t+1;

       of.numtrk = of.numpat*of.numchn;

       /* seek across eventual new data */
       _mm_fseek(modfp,mh1->headerlen-(869+mh1->stlen),SEEK_CUR);

       /* alloc track and pattern structures */
       if(!AllocTracks()) return 0;
       if(!AllocPatterns()) return 0;

       for(t=0; t<of.numpat; t++)
       {   UBYTE rows=0,tempo;

           memset(pat,0,16*256*sizeof(FARNOTE));
           if(mh2->patsiz[t])
           {   rows  = _mm_read_UBYTE(modfp);
               tempo = _mm_read_UBYTE(modfp);

               crow = pat;
               for(u=mh2->patsiz[t]-2; u; u--, crow++)
               {   crow->note = _mm_read_UBYTE(modfp);
                   crow->ins  = _mm_read_UBYTE(modfp);
                   crow->vol  = _mm_read_UBYTE(modfp);
                   crow->eff  = _mm_read_UBYTE(modfp);
               }
               
               if(_mm_feof(modfp))
               {   _mm_errno = MMERR_LOADING_PATTERN;
                   return 0;
               }

               of.pattrows[t] = rows+2;
               crow = pat;
               for(u=16; u; u--)
                  if(!(of.tracks[tracks++] = FAR_ConvertTrack(crow,rows+2))) return 0;
           }
       }

       /* read sample map */
       if(!_mm_read_UBYTES(smap,8,modfp))
       {   _mm_errno = MMERR_LOADING_HEADER;
           return 0;
       }

       /* count number of samples used */
       of.numins = 0;
       for(t=0; t<64; t++)
          if(smap[t>>3] & (1 << (t&7)))  of.numins++;
       of.numsmp = of.numins;             

       /* alloc sample structs */
       if(!AllocSamples()) return 0;
       q = of.samples;

       for(t=0; t<64; t++)
       {  if(smap[t>>3] & (1 << (t&7)))
           {  _mm_read_SBYTES(s.samplename,32,modfp);
              s.length   = _mm_read_I_ULONG(modfp);
              s.finetune = _mm_read_UBYTE(modfp);
              s.volume   = _mm_read_UBYTE(modfp);
              s.reppos   = _mm_read_I_ULONG(modfp);
              s.repend   = _mm_read_I_ULONG(modfp);
              s.type     = _mm_read_UBYTE(modfp);
              s.loop     = _mm_read_UBYTE(modfp);

              q->samplename = DupStr(s.samplename,32);
              q->length     = s.length;
              q->loopstart  = s.reppos;
              q->loopend    = s.repend;
              q->volume     = 64;
              q->speed      = 8363;

              q->flags=SF_SIGNED;
              if(s.type&1) q->flags|=SF_16BITS;
              if(s.loop) q->flags|=SF_LOOP;

              q->seekpos = _mm_ftell(modfp);
              _mm_fseek(modfp,q->length,SEEK_CUR);
           }
           q++;
       }
       return 1;
}


CHAR *FAR_LoadTitle(void)
{
   CHAR s[40];

   _mm_fseek(modfp,4,SEEK_SET);
   if(!_mm_fread(s,40,1,modfp)) return NULL;
   
   return(DupStr(s,40));
}


MLOADER load_far =
{   NULL,
    "FAR",
    "Portable FAR loader v0.1",
    FAR_Init,
    FAR_Test,
    FAR_Load,
    FAR_Cleanup,
    FAR_LoadTitle
};

